/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFDC
  \brief for DCs downstream the dipole magnet. The construction of the DC depends
  on the input yaml config file.
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2021-10-28
  \date 2021-12-10 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2021-10-28   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <cmath>
#include <iostream>
#include <cstring>
#include <TClonesArray.h>
#include <TTree.h>
#include "ETFRun.h"
#include "ETFTrackManager.h"
#include "ETFDCHit.h"
#include "ETFMath.h"
#include "ETFMsg.h"
#include "ETFDC.h"
#include "ETFRawChannel.h"
#include "ETFVTrack.h"
#include "ETFYaml.h"
#include "ETFAnalyzer.h"
#include "ETFPostMagTracker.h"
#include "ETFParaManager.h"

using std::cout;
using std::endl;
using std::string;

static constexpr double DEGREE = ETFMath::DEGREE();
static constexpr double DEG30 = 30.*DEGREE;

ETFDC::ETFDC(const char *name) : ETFVDC(name), fPhiAvrg(-999.){
  // configure the DC //
  fNL = fChIdConfig["X"].size() * 16;
  fSenseWireDistance = 10.; fMaxDL = fSenseWireDistance/2.;
  fLayerSpace12 = 10.; fLayerSpaceXY = 40.8;
  fStaggerY12 = fStagger12 = -4.;
  // fY0Min = fX0Min = -393.; // 7. - fSenseWireDistance*(fNL/2)
  fY0Min = fX0Min = 7. - fSenseWireDistance/2.*fNL;

  // take care of DC reverse in pion exp //
  // if(!strcmp(name, "dcm2")){
  //   fY0Min = fX0Min = 7. - fSenseWireDistance/2.*fNL - 10.;
  // } // end if

  const char *xuv[] = {"X", "U", "V"};
  for(const char *c : xuv){
    if(fChIdConfig[c]){
      type[2] = 0;
      for(const auto &chid : fChIdConfig[c]){
        FillChIdMap(chid.as<int>());
        type[2]++; // cable id
      } // end for over cables
    } // end if
    else ETFMsg::Warn("ETFDC", "ctor: No %c chId in yaml config file", c);
    type[0]++; // XUV
  } // end for over XUV
  // pass user trig bound parameters //
  const ETFYaml &trigBConfig = (*fpm->GetMisPara())["dcTrigB"];
  if(trigBConfig){
    fTrigLB = trigBConfig[0].as<double>();
    fTrigHB = trigBConfig[1].as<double>();
  } // end if
  const ETFYaml &DTBConfig = (*fpm->GetMisPara())["dcDTB"];
  if(DTBConfig){
    fDTLB = DTBConfig[0].as<double>();
    fDTHB = DTBConfig[1].as<double>();
  } // end if

  for(int i = 0; i < 3; i++) for(int j = 0; j < 2; j++){
    fHits[i][j] = new TClonesArray("ETFDCHit");
    char name_cl[128]; sprintf(name_cl, "%s%s%d", name, xuv[i], j);
    fHits[i][j]->SetName(name_cl);
  } // end embedded for
} // end ctor

ETFDC::~ETFDC(){
  for(auto &h : fHits) for(auto &l : h) if(l) delete l;
} // end the dtor

void ETFDC::Register(){
  for(auto &p : fHits) for(auto &l : p) if(l) fTree->Branch(l->GetName(), &l);
} // end member function Register

TClonesArray *ETFDC::GetHitsArray(DCType typeDC, int x12){
  if(x12 < 0 || x12 >= 3) ETFMsg::Error("ETFDC", "GetHitsArray: x12 out of range");
  return fHits[short(typeDC)%3][x12%2];
} // end member function GetHitsArray

// initialize the detector
void ETFDC::Initialize(){
  ETFDetector::Initialize();
  for(auto &h : fHits) for(auto &l : h) if(l) l->Clear();
} // end member function Initialize

// fill channel map for a 32-ch LVDS cable
// type[3]; ///< XUV, X1-X2, [cableId]
void ETFDC::FillChIdMap(int chId0){
  if(chId0 < 0) return;
  for(int i = 0; i < 2; i++) for(int j = 0; j < 16; j++){ // i: x12, j: wire id
    int jj = j;
    const int wireId = type[2]*16+j, cableId = wireId / 16; // 0-1-2-3-4
    // chid maps are reversed for pre-amps in the downside of the DCs //
    // namely the U's last 2 and V's first 2 cables //
    if((1 == type[0] && cableId >= 3) || (2 == type[0] && cableId <= 1))
      jj = 15 - jj;
    SetChMap(chId0-i*16-jj+31, uid(type[0], i, wireId));
  } // end double for
} // end member function FillCableChMap

void ETFDC::AddHit(const ETFRawChannel *ch, int wireId, DCType typeDC){
  if(DCType::kX != typeDC) return; // XXX XXX XXX - disable U and V for now - Jun 21, 2024
  static const char *cc = "xuv"; // = X, U or V according to typeDC
  static const double cos30 = cos(DEG30), tan30 = tan(DEG30);
  const int xuv = int(typeDC);
  const int x12 = wireId / fNL, id = wireId % fNL;
  ETFDCHit *hit =
    static_cast<ETFDCHit *>(fHits[xuv][x12]->ConstructedAt(fHits[xuv][x12]->GetEntriesFast()));
  hit->Initialize();
  hit->SetTChannel(ch);
  hit->SetLayerId(fDCId*2 + x12); // 0-1-2-3-4-5: DC0X1-X2--DC1X1-X2--DC2X1-X2
  hit->SetWireId(id);
  hit->SetDelay(fDelay);
  // set position in local frame, (x,y,z) is the projection of the wire in the layer's norml plane
  if(DCType::kX == typeDC){
    hit->SetX(fX0Min + fSenseWireDistance*id + fStagger12*x12);
    hit->SetY(0.);
    hit->SetZ(fLayerSpace12*x12);
  } // end if
  if(DCType::kU == typeDC){
    hit->SetX((fX0Min + fSenseWireDistance*id + fStagger12*x12)*cos30);
    hit->SetY(-hit->GetX()*tan30);
    hit->SetZ(fLayerSpace12*x12 - fLayerSpaceXY);
  } // end if
  if(DCType::kV == typeDC){
    hit->SetX((fX0Min + fSenseWireDistance*id - 4. - fStagger12*x12)*cos30);
    hit->SetY(hit->GetX()*tan30);
    hit->SetZ(fLayerSpace12*x12 + fLayerSpaceXY);
  } // end if
  hit->SetName(ETFMsg::Form("%s_%c%d_id%d", GetName(), cc[xuv], x12 + 1, id));

  TransformToLabRef(hit);
  SetHitProjection(hit, typeDC);
} // end member function AddHit

// set the wire's projection to its normal plane
void ETFDC::SetHitProjection(ETFDCHit *hit, DCType typeDC){
  if(DCType::kX == typeDC){
    hit->SetProjection(hit->GetPosition());
    return; // X wires does not need backward rotation
  } // end if

  const double *p = hit->GetPosition();
  if(-999. == fPhiAvrg){
    fPhiAvrg = ETFRun::Instance()->GetAnalyzer()->phiAvrg();
    if(-999. == fPhiAvrg) fPhiAvrg = GetPhi();
  } // end if
  static double ct, st;
  static double proj[3]; // projection of the wire center position to the wire's normal plane
  if(DCType::kU == typeDC){ ct = cos(-DEG30); st = sin(-DEG30); }
  else{ ct = cos(DEG30); st = sin(DEG30); }
  const double cp = cos(fPhiAvrg), sp = sin(fPhiAvrg);
	
  // Au = Rz[-theta(: +-30)].Ry[-fPhiAvrg].A;
	proj[0] = ct*cp  *p[0] +st *p[1] -ct*sp *p[2];
	proj[1] = -st*cp *p[0] +ct *p[1] +st*sp *p[2];
	proj[2] = sp     *p[0] /*+0.*p[1]*/+cp  *p[2];

  hit->SetProjection(proj);
} // end member function SetHitProjection

// r = r(t); drift distance - drift time relation
double ETFDC::SpaceTimeRelation(double t){
  // usually STR at different HVs are so different as worth special treatment
  // be it a garfield simulation, or user-refined tuning based on experiment data analysis
  // static const double T0 = fDTHB*0.7; // the fitted rt relation is invalid for t > T0
  // const double T1 = fDTHB*0.85;
  const int POLN = fvstr.size();
  double r = 0.;

  if(!POLN) r = ETFVDC::SpaceTimeRelation(t); // a parabola-form STR
  else{
    // if(t < T0) for(int i = POLN; i--;) r += fvstr[i]*pow(t, i);
    // else if(t <= T1){ // we use a linear smoothing here with (T0, r0) -> (T1, fMaxDL)
    //   double r0 = 0.;
    //   for(int i = POLN; i--;) r0 += fvstr[i]*pow(T0, i);
    //   r = (fMaxDL - r0) / (T1 - T0) * (t - T1) + fMaxDL;
    // } // end inner else
    // else r = fMaxDL;
    for(int i = POLN; i--;) r += fvstr[i]*pow(t, i);
  } // end else
  if(r < 0. || t <= fDTLB) r = 0.;
  if(r > fMaxDL || t > 200.) r = fMaxDL;

  return r;
} // end member function SpaceTimeRelation

void ETFDC::DrawHits(){
  for(auto &h : fHits[0]) if(h) for(const auto &p : *h)
    dynamic_cast<ETFDCHit *>(p)->Draw();
} // end member function DrawHits

void ETFDC::Print(){
  if(!fIsFired) return;
  ETFDetector::Print();
  static const char *dcType[] = {"DCType::kX", "DCType::kU", "DCType::kV"};
  static const char *x12[] = {"1", "2"};
  static const char *xuv[] = {"X", "U", "V"};
  int i = 0, j = 0;
  cout << "Number of sense wires per layer: " << fNL << endl;
  for(auto &h : fHits){
    cout << "\033[33;1m___________DCType: " << dcType[i] << "___________\033[0m" << endl;
    j = 0;
    for(auto &l : h){
      cout << "\033[31;1m-------------- " << xuv[i] << x12[j] << " --------------\033[0m" << endl;
      for(auto p : *l) dynamic_cast<ETFDCHit *>(p)->Print();
      j++;
    } // end for over x1-x2
    i++;
  } // end for over xuv
} // end member function Print

// finish the processing of all the assigned data
void ETFDC::PostAssign(){
  ETFVDC::PostAssign();
  for(auto &i : fHits) for(auto &j : i) for(auto b : *j)
    dynamic_cast<ETFDCHit *>(b)->Assign();
} // end member function PostAssign

// filter all layers
void ETFDC::Filter(){
  // for(auto &i : fHits) for(auto &c : i) Filter(c);
  // just filter X for now
  Filter(fHits[0][0]);
  Filter(fHits[0][1]);
} // end member function Filter
